/** @jsxImportSource @emotion/react */
import {jsx,  css, SerializedStyles } from "@emotion/react";
import * as React from "react";
import { SpringValue,useSprings } from "@react-spring/web";
import { animated } from "@react-spring/web";
import {CSSProperties} from "react";
import {useDrag} from "@use-gesture/react";
import {safeBind} from "./Hooks/compose-bind";
import { Text } from "./Text";
import { Button } from "./Button";
import { IconChevronLeft } from "./Icons";
import { useTheme } from "./Theme/Providers";
import {useMMenuBarOcup} from "./MainMenuBar";



export interface StackContextType {
    //表示Stack下沉的层次序值：目前在第几层的索引。0=首层的, 1=点击进入了第二层的内容，=2...#更多层次就不好准备数据和页面?，真的还不如直接定义数组形式静态型页面路由器。
    index: number;
    activeIndex: number;
    //下沉的层次正在被点击切换： 点击沉下去或者返回时刻回调，#允许准备数据。
    changeIndex: (index: number) => void;

    active: boolean;
    // dragging: boolean;
    navHeight: number;
    overlay?: SpringValue<number>;
    opacity?: SpringValue<number>;
    transform?: SpringValue<number>;
}

export const StackContext = React.createContext<StackContextType>({
    //我这是Stack哪一个层次的
    index: 0,
    //当前正在显示哪一个层次
    activeIndex: 0,
    // dragging: false,
    navHeight: 50,
    active: false,
    //正在下沉的 或 点后退按钮，回调
    changeIndex: () => {}
});

export interface StackTitleProps {
    /** The pane title which is centered */
    title?: React.ReactNode;
    /** The title of the back button */
    backTitle?: React.ReactNode;
    /** Content that appears to the left of the title */
    contentBefore?: React.ReactNode;
    /** Content that appears to the right of the title */
    contentAfter?: React.ReactNode;
    style?: React.CSSProperties;
    backButton?: React.ReactNode;
    //后面按钮区域样式
    afterStyle?: CSSProperties;
}

const ExternalTitle = (props) => {
    //不能使用if(!props.transform.to) return null;
    return  <div className="StackTitle__heading"
        style={{
            margin: "0.25rem",
            textOverflow: "ellipsis",
            whiteSpace: "nowrap",
            overflow: "hidden",
            opacity: props.opacity,
            transform: props?.transform?.to && props?.transform?.to(x => `translateX(${x * 0.85}%)`)
        }}
        children={props.title}
    />
}

const ExternalAfter = (props) => {
    return <div className="StackTitle__content-after"
        style={{
            display: "flex",
            textAlign: "right",
            textOverflow: "ellipsis",
            whiteSpace: "nowrap",
            opacity: props.opacity,
        }}
        css={{
            ...props.afterStyle
        }}
    >
        {props.contentAfter}
    </div>
}

const ellipsis = {
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    overflow: "hidden"
} as any; // eh?

const ExternalBefore = (props) => {
    const theme = useTheme();
    return <div  className="StackTitle__Content-before"
        style={{
            opacity: props.opacity,
            ...(ellipsis as any),
            transform: props?.transform?.to && props?.transform?.to(x => `translateX(${x * 0.7}%)`),
        } as any}
        css={{
            flex: "0 0 4.5rem",
            [theme.mediaQueries.lg]: {
                flex: "0 0 5.5rem",
            }
        } as any}
    >
        {props.index > 0 &&
            !props.contentBefore &&
            (props.backButton || (
                <button
                    onClick={() => {
                        props.changeIndex(props.index - 1);
                    }}
                    style={{
                        display: "flex",
                        alignItems: "center",
                        border: "none",
                        background: "none",
                        //margin: "0.25rem"
                    }}
                    className="StackTitle__button-back"
                >
                    <IconChevronLeft size={24} color="currentColor" />
                    {props.backTitle}
                </button>
            ))}
        {props.contentBefore}
    </div>
}

//这里用的<animated.div就是变动样式的Div: 渐进变动CSS，浪费CPU算力。
function OriginalStackTitle({
                                title,
                                backTitle = "Back",
                                contentAfter,
                                contentBefore,
                                backButton,
                                style,
                                afterStyle
                            }: StackTitleProps) {
    const {
        navHeight,
        index,
        active,
        changeIndex,
        opacity,
        transform
    } = React.useContext(StackContext);

    if (!transform || !opacity) {
        throw new Error("StackTitle must be used within a Stack component");
    }
    const AnimatedTitle = animated(ExternalTitle);
    const AnimatedAfter = animated(ExternalAfter);
    const AnimatedBefore = animated(ExternalBefore);

    return (
        <div
            className="StackTitle"
            aria-hidden={!active}
            style={{
                pointerEvents: active ? "auto" : "none",
                zIndex: 10,
                position: "absolute",
                top: 0,
                left: 0,
                right: 0
            }}
        >
            <div
                style={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "space-between",
                    height: navHeight + "px",
                    boxSizing: "border-box",
                    ...style
                }}
            >
                <AnimatedBefore opacity={opacity} transform={transform} index={index} changeIndex={changeIndex} contentBefore={contentBefore}
                                backButton={backButton} backTitle={backTitle}/>
                <AnimatedTitle opacity={opacity} transform={transform} title={title}/>
                <AnimatedAfter opacity={opacity} afterStyle={afterStyle} contentAfter={contentAfter}/>
            </div>
        </div>
    );
}


export function StackTitle({
                               title,
                               backTitle = "Back",
                               ...other
                           }: StackTitleProps) {
    const {index, changeIndex} = React.useContext(StackContext);

    return (
        <OriginalStackTitle
            backButton={
                <Button
                    variant="ghost"
                    onPress={() => changeIndex(index - 1)}
                    iconBefore={<IconChevronLeft />}
                >
                    {backTitle}
                </Button>
            }
            title={
                title ? (
                    <Text gutter={false} variant="h6">
                        {title}
                    </Text>
                ) : null
            }
            {...other}
        />
    );
}


export interface StackItemProps extends React.HTMLAttributes<HTMLDivElement> {
    generateShadow?: (x: number) => string;
}
/**StackItem用；
 * 已经设置好的style，若想自己修改掉：从外部注入样式，使用style才有效， 若用css无法做到。
 css={{ marginBottom: `${barHeight}`, }}
 style={{ position: undefined, boxShadow:undefined }}
 * */
function OriginalStackItem({
                               style,
                               generateShadow,
                               children,
                               className = "",
                               ...other
                           }: StackItemProps) {
    const { index, opacity, overlay, active, transform } = React.useContext(
        StackContext
    );

    if (!transform || !opacity || !overlay) {
        throw new Error("Stack must be used as a child of StackManager");
    }

    const cx = `StackItem StackItem-${index} ${
        active ? "StackItem-active" : ""
    } ${className}`;

    return (
        <animated.div
            className={cx}
            aria-hidden={!active}
            style={ {
                background: "white",
                position: "absolute",
                top: 0,
                left: 0,
                right: 0,
                bottom: 0,
                boxShadow: opacity.to(x =>
                    generateShadow
                        ? generateShadow(x)
                        : `0 0 12px -2px rgba(160,160,160,${x})`
                ),
                transform: transform.to(x => `translateX(${x}%)`),
                "@media print": {
                    boxShadow: 'unset',
                },
                ...style,
            }as any }
            {...(other as any)}
        >
            {children}
            <animated.div
                style={{
                    pointerEvents: "none",
                    background: "#f1f3f5",
                    opacity: overlay,
                    position: "absolute",
                    top: 0,
                    left: 0,
                    right: 0,
                    bottom: 0
                }}
            />
        </animated.div>
    );
}



export function StackItem(props: StackItemProps) {
    const theme = useTheme();
    const dark = theme.colors.mode === "dark";
    //x: 输入 0% -- 100%  取值来自 opacity 动画;
    function generateShadow(x: number) {
        return dark
            ? `rgba(0, 0, 0, ${x}) 0px 0px 12px -2px`
            : `rgba(160, 160, 160, ${x}) 0px 0px 12px -2px`;
    }

    return (
        <OriginalStackItem
            generateShadow={generateShadow}
            style={{
                background: undefined
            }}
            css={{
                backgroundColor: theme.colors.background.default,
                "& .StackItem__overlay": {
                    backgroundColor: theme.colors.background.tint1 + "!important"
                }
            }}
            {...props}
        />
    );
}


/**Get position of stack items; 获取 Spring配置;
 * 参数 i： 针对哪一个Stack 层次。  currentIndex=当前Stack层次，
 * overlay配置项: #实际等同StackItem__overlay哪一个Div修饰的opacity属性；overlay和opacity修饰两个不同Div。
 * left对应 transform：translateX(${x}% 实际百分比。X轴的水平位移。
 *假如Stack有3层次0，1，2： 实际页面是2层的Div页面并排的。依赖于translateX进行隐藏。 后面Stack层次的页面覆盖前面DIV页面的显示。
 * 使用transform: translateX(100%)隐藏; translateX(0%)显示。
 */
function getAnimationValues(i: number, currentIndex: number) {
    // current    translateX(0%) 正常的位置显示。
    if (i === currentIndex) {
        return { left: 0, immediate: false, opacity: 1, overlay: 0 };
    }
    // next  在底下的； translateX(100%) 隐藏掉, 往右边移出100%宽度
    else if (i > currentIndex) {
        return { left: 100, immediate: false, opacity: 0, overlay: 0 };
    }
    // previous 上一个层次的： ？？向左边移动 一半left: -50,  50%宽度页面; 改成-100
    return { left: -100, immediate: false, opacity: 0, overlay: 1 };
}

/**
 * Stack manager
 */

interface StackItemList {
    title?: React.ReactNode;
    content: React.ReactNode;
}

export interface StackProps extends React.HTMLAttributes<HTMLDivElement> {
    //当前处于哪一个层次的：0, 1 # 0=首层的, 1=点击进入了第二层的内容;  2,...
    index: number;
    //下沉的层次正在被点击切换： 点击沉下去或者返回时刻回调，#允许准备数据。
    onIndexChange: (index: number) => void;
    //每一层的： 导航条 和 内容区域。
    items: StackItemList[];

    disableNav?: boolean;
    navHeight?: number;
    disableScroll?: boolean;
    //给头部<NavBar>增加样式注入  ,position修改不了啊
    navStyle?: SerializedStyles;
    //要不要允许手势操作处理，允许拉拽？
    noBind?: boolean;
    //电脑屏为了确保search input cancel触发最好改triggerAllEvents: false,
    // searchCancel?: boolean;
}

/**下沉揭示关联页面的组件。层次越多越不好配置。单一个层次的组件最好是参数化的内容。
 * */
export const Stack = (
    {
        style,
        children,
        index,
        disableNav,
        disableScroll = true,
        navHeight = 50,
        items,
        navStyle,
        onIndexChange,
        noBind=false,
        ...other
    }:StackProps) => {
    const theme = useTheme();
    const ref = React.useRef(null);       //整個的Stack
    const levelsum = items.length;
    // useScrollLock(dragging && disableScroll);
    const {touchScr, } = useMMenuBarOcup(false);

    //数组形式版本的useSpring();  set default positions
    const [springs, setSprings] = useSprings(levelsum, i => {
        return getAnimationValues(i, index);
    });

    React.useEffect(() => {
        setSprings(i => getAnimationValues(i, index));
    }, [index, setSprings]);
    /*触发的时机提前了：拉拽active=true就要触发，其它都是最后一个事件last=true&&active=false才会触发动作。
    拉拽也没有设置跟随同步移动的动画Div。触发动作才执行一步到位useEffect动画。
    * */
    const bindDrag = useDrag((state) => {
            const { active, movement: [mx,my], direction: [xDir,yDir] , cancel,
                swipe:[swpx,], last,  canceled,tap, event } = state;
            //event.stopPropagation();  //加了没法让菜单点外区关闭
            //点击的跳过。 &&  mx> 5不依赖于width大小都能触发; active就可以触发了=提早执行时机。 #不依靠direction[]swipe[]这两组有问题啊？
            if(active  && !tap &&  mx> 5  &&  Math.abs(mx)>Math.abs(my) ) {
                if(index>0)
                    onIndexChange(index - 1);
                cancel();
            }
            //setSprings(i => getAnimationValues(i, index)); 没必要了，useEffecct()修正;
        },
        {
            swipe: {velocity: 0.01},
            filterTaps: true,
            pointer: {mouse: !touchScr },
            // pointer: {touch: true, mouse: !touchScr },     //嵌套Pager拉拽避免拉动父辈层和search input的cancel在电脑模式点击能清空的要求正好冲突了。
            // triggerAllEvents: touchScr,     //电脑屏为了确保serach input cancel触发最好改triggerAllEvents: false,
            enabled: !noBind,
        });

    //上面配置：第三代pointer: {touch: false,
    //外面福建css style默认是放在{...other}这个DIV的。 //    bindDrag(),
    return (
        <React.Fragment>
            <div  className="Stack"
                  ref={ref}
                  style={{
                      overflow: "hidden",
                      display: "flex",
                      flexDirection: "column",
                      touchAction: 'none',
                      ...style
                  }}
                  {...safeBind(bindDrag(), other)}
            >
                {!disableNav && (
                    <div  className="Stack__nav"
                          style={{
                              background: theme.colors.background.tint1,
                              boxShadow: theme.shadows.xs,
                          }}
                          css={[
                              {
                                  position: "relative",
                                  zIndex: 25,
                                  height: `${navHeight}px`,
                                  "@media print": {
                                      boxShadow: 'unset',
                                  },
                              },
                              navStyle,
                          ]}
                    >
                        {   //这里StackContext.Provider竟然用了两处的，并且还是数据.map: #起码2*2个Provider出处。
                            springs.map((props, i) => {
                                return (
                                    <StackContext.Provider
                                        key={i}
                                        value={{
                                            index: i,
                                            navHeight,
                                            activeIndex: index,
                                            active: i === index,
                                            overlay: props.overlay,
                                            opacity: props.opacity,
                                            transform: props.left.to(x => clamp(x)) as any,
                                            changeIndex: onIndexChange
                                        }}
                                    >
                                        {items[i].title}
                                    </StackContext.Provider>
                                );
                            })}
                    </div>
                )}
                <div  className="StackCont"
                      style={{
                          position: "relative",
                          overflow: "hidden",
                          background: "white",
                          flex: 1
                      }}
                >
                    {     //类型来自针对{left: 100, immediate: false, opacity: 0, overlay: 0}做的useSpring((i)=>{left,overlay})，每一个Stack层次都要render;
                        springs.map((props, i) => {
                            return (
                                <StackContext.Provider
                                    key={i}
                                    value={{
                                        index: i,
                                        activeIndex: index,
                                        navHeight,
                                        active: i === index,
                                        overlay: props.overlay,
                                        opacity: props.opacity,
                                        transform: props.left.to(x => clamp(x)) as any,
                                        changeIndex: onIndexChange
                                    }}
                                >
                                    {items[i].content}
                                </StackContext.Provider>
                            );
                        })}
                </div>
            </div>
        </React.Fragment>
    );
};

// ensure values don't exceed our bounds
// when dragging
function clamp(x: number) {
    if (x > 100) {
        return 100;
    }
    if (x < -50) {
        //旧的 -50
        return -100;
    }
    return x;
}


/* 旧版
export function Stack(props: StackProps) {
    const theme = useTheme();
    return (
        <OriginalStack
            css={{
                ".Stack__nav": {
                    background: theme.colors.background.tint1,
                    boxShadow: theme.shadows.xs
                }
            }}
            {...props}
        />
    );
} */
